import { IMDCtrlController, IMDCtrlStore } from '@/core';
import {
	ICodeListItem,
	IContext,
	ICtrlActionResult,
	IHttpResponse,
	IMDCtrlAbility,
	IMDCtrlControllerParams,
	IMDCtrlModel,
	IParam,
	MDCtrlActionType,
} from '@/core/interface';
import { DataTypeUtil, deepCopy, ExportExcelUtil, typeOf } from '../../utils';
import { DECtrlController } from './de-ctrl-controller';
/**
 * 部件控制器
 *
 * @export
 * @class CtrlController
 * @implements {IMDCtrlController}
 */
export class MDCtrlController<
		T,
		S extends IMDCtrlStore,
		A extends IMDCtrlAbility
	>
	extends DECtrlController<T | MDCtrlActionType, S, A>
	implements IMDCtrlController<S, A>
{
	/**
	 * 多数据部件模型
	 *
	 * @protected
	 * @type {IMDCtrlModel}
	 * @memberof MDCtrlController
	 */
	protected declare model: IMDCtrlModel;

	/**
	 * 是否单选
	 *
	 * @protected
	 * @type {boolean}
	 * @memberof MDCtrlController
	 */
	protected singleSelect!: boolean;

	/**
	 * 默认选择第一项
	 *
	 * @protected
	 * @type {boolean}
	 * @memberof MDCtrlController
	 */
	protected selectFirstDefault = false;

	/**
	 * 处理部件初始化
	 *
	 * @protected
	 * @param { IMDCtrlControllerParams<T, A>} params
	 * @memberof MDCtrlController
	 */
	protected processCtrlInit(params: IMDCtrlControllerParams<T, A>) {
		super.processCtrlInit(params);
		this.singleSelect = params.singleSelect === true;
		this.selectFirstDefault = params.selectFirstDefault === true;
		Object.assign(this.store, {
			curPage: 1,
			limit: params.model.paging?.defaultPageSize
				? params.model.paging.defaultPageSize
				: 20,
			totalRecord: 20,
			data: [],
			selections: [],
			sort: '',
		});
	}

	/**
	 * 获取数据
	 *
	 * @return {*}  {IParam[]}
	 * @memberof MDCtrlController
	 */
	public getData(): IParam[] {
		//  分组列不参与数据逻辑交互
		const data: Array<IParam> = this.store.selections.filter(
			(select: IParam) => !select.isGroupItem
		);
		const exposeData: Array<IParam> = [];
		this.computeExposeData(data, exposeData);
		return exposeData;
	}

	/**
	 * 处理加载数据变化
	 *
	 * @protected
	 * @param {IParam[]} data
	 * @memberof MDCtrlController
	 */
	protected handleLoadDataChange(data: IParam[]) {
		this.store.data = data;
	}

	/**
	 * 多数据部件数据加载
	 *
	 * @param {IParam} [opts={}]
	 * @param {boolean} [pageReset=false]
	 * @return {*}  {Promise<IHttpResponse>}
	 * @memberof MDCtrlController
	 */
	public async load(opts: IParam = {}): Promise<ICtrlActionResult> {
		const { context, viewParams, limit } = this.store;
		const { fetchAction } = this.actions;
		if (!fetchAction) {
			console.warn(App.ts('widget.md.notconfig.fetchaction', '未配置加载行为'));
			return { ok: false, data: this.getData(), rowData: { status: 500 } };
		}
		if (opts && opts.srfpagereset) {
			this.store.curPage = 1;
			delete opts.srfpagereset;
		}
		const { paging, noSort, sortDir, sortField, name } = this.model;
		const arg: any = {};
		const queryParams: IParam = {};
		//  开启分页
		if (paging) {
			Object.assign(queryParams, { page: this.store.curPage - 1, size: limit });
		}
		//  排序
		if (!noSort && !opts.srfsortreset) {
			if (this.store.sort) {
				Object.assign(queryParams, { sort: this.store.sort });
			} else {
				if (sortField) {
					Object.assign(queryParams, {
						sort: `${sortField},${sortDir ? sortDir : 'asc'}`,
					});
				}
			}
		}
		delete opts.srfsortreset;
		const parentData: IParam = {};
		this.emit('beforeLoad', [parentData]);
		//	视图查询参数
		if (parentData && Object.keys(parentData).length > 0) {
			Object.assign(queryParams, parentData);
		}
		//	额外查询参数
		if (opts && Object.keys(opts).length > 0) {
			Object.assign(queryParams, opts);
		}
		const tempViewParams = Object.assign(deepCopy(viewParams), queryParams);
		Object.assign(arg, { viewParams: tempViewParams });
		const tempContext = deepCopy(context);
		const res = await this.beforeAsyncAction('load', tempContext, arg);
		if (!res.ok) {
			return { ok: false, data: this.getData(), rowData: { status: 500 } };
		}
		// 合并界面逻辑执行参数
		if (res.data && res.data.length > 0) {
			Object.assign(arg, res.data[0]);
		}
		// TODO 行为前
		try {
			const response: IHttpResponse = await this.ctrlService.search(
				fetchAction,
				tempContext,
				arg
			);
			if (!response.success) {
				await this.afterAsyncAction('load', response);
				return {
					ok: false,
					data: this.getData(),
					rowData: response,
				};
			}
			const res = await this.afterAsyncAction('load', response);
			if (!res.ok) {
				return { ok: false, data: this.getData(), rowData: { status: 500 } };
			}
			const { total } = response as IParam;
			if (total || total === 0) {
				this.store.totalRecord = total;
			}
			this.handleLoadDataChange(response.data);
			this.emit('load', response.data);
			return {
				ok: true,
				data: this.getData(),
				rowData: response,
			};
		} catch (error: any) {
			await this.afterAsyncAction('load', error);
			return {
				ok: false,
				data: this.getData(),
				rowData: error,
			};
		}
	}

	/**
	 * 处理分组
	 *
	 * @protected
	 * @return {*}
	 * @memberof MDCtrlController
	 */
	protected handleGroup() {
		const { enableGroup, groupMode } = this.model;
		if (!enableGroup) {
			return;
		}
		if (groupMode === 'AUTO') {
			this.handleAutoGroup();
		} else if (groupMode === 'CODELIST') {
			this.handleCodeListGroup();
		}
	}

	/**
	 * 自动分组
	 *
	 * @protected
	 * @memberof MDCtrlController
	 */
	protected async handleAutoGroup() {
		const { data } = this.store;
		if (data.length === 0) {
			return;
		}
		//  分组属性标识
		const field = this.model.groupField as string;
		//  分组属性代码表
		const fieldCodeList = this.model.groupFieldCodeListTag;
		//  分组列标识
		const dataIndex = this.model.groupDataIndex as string;
		const resultMap: Map<string, IParam[]> = new Map();
		const codeItems: IParam[] = [];
		if (fieldCodeList) {
			const result = await App.getAppCodeListHelper().getCodeListItems(
				fieldCodeList
			);
			if (result.ok) {
				codeItems.push(...result.data);
			}
		}
		data.forEach((item: IParam) => {
			const items = resultMap.get(item[field]) || [];
			items.push(item);
			resultMap.set(item[field], items);
		});
		const resultItems: IParam[] = [];
		resultMap.forEach((value: IParam[], key: string) => {
			const item =
				codeItems.length > 0
					? codeItems.find((item: IParam) => item.value === key)
					: { text: key };
			resultItems.push({
				key: `group-item__${key}`,
				srfkey: `group-item__${key}`,
				srfmajortext: item ? item.text : App.ts('widget.md.other', '其他'),
				[dataIndex]: item ? item.text : App.ts('widget.md.other', '其他'),
				isGroupItem: true,
				children: value,
			});
		});
		this.store.data = resultItems;
	}

	/**
	 * 代码表分组
	 *
	 * @protected
	 * @return {*}
	 * @memberof MDCtrlController
	 */
	protected async handleCodeListGroup() {
		//  分组代码表标识
		const codeListTag = this.model.groupCodeListTag as string;
		//  分组属性标识
		const field = this.model.groupField as string;
		//  分组列标识
		const dataIndex = this.model.groupDataIndex as string;
		if (!codeListTag) {
			return;
		}
		const { context, viewParams } = this.store;
		const result = await App.getAppCodeListHelper().getCodeListItems(
			codeListTag,
			context,
			viewParams
		);
		if (!result.ok || result.data.length === 0) {
			return;
		}
		const { data } = this.store;
		const keys: (string | number)[] = result.data.map(
			(d: ICodeListItem) => d.value
		);
		const resultItems: IParam[] = [];
		result.data.forEach((item: ICodeListItem) => {
			const items: IParam[] = data.filter(
				(d: IParam) => d[field] === item.value
			);
			resultItems.push({
				key: `group-item__${item.value}`,
				srfkey: `group-item__${item.value}`,
				srfmajortext: item.text,
				value: item.value,
				[dataIndex]: item.text,
				isGroupItem: true,
				children: items,
			});
		});
		const otherItems = data.filter(
			(d: IParam) => keys.indexOf(d[field]) === -1
		);
		if (otherItems.length > 0) {
			resultItems.push({
				key: `group-other-item`,
				srfkey: `group-other-item`,
				srfmajortext: App.ts('widget.md.other', '其他'),
				[dataIndex]: App.ts('widget.md.other', '其他'),
				isGroupItem: true,
				children: otherItems,
			});
		}
		this.store.data = resultItems;
	}

	/**
	 * 设置默认选中
	 *
	 * @protected
	 * @memberof MDCtrlController
	 */
	protected handleDefaultSelect() {
		const { viewParams } = this.store;
		//  默认选中第一项
		if (this.selectFirstDefault) {
			const { data } = this.store;
			if (data && data.length > 0) {
				this.store.selections = [data[0]];
				this.emit('selectionChange', this.getData());
			}
		}
		//  存在选中数据
		if (
			viewParams &&
			viewParams.srfselecteddata &&
			viewParams.srfselecteddata.length > 0
		) {
			const selections: IParam[] = [];
			viewParams.srfselecteddata.forEach((select: IParam) => {
				if (select.srfkey) {
					const item = this.store.data.find(
						(i: IParam) => i.srfkey === select.srfkey
					);
					if (item) {
						selections.push(item);
					}
				}
			});
			if (selections.length > 0) {
				this.store.selections = this.singleSelect
					? [selections[0]]
					: selections;
				this.emit('selectionChange', this.getData());
			}
		}
	}

	/**
	 * 处理排序变化
	 *
	 * @param {string} sortField
	 * @param {('asc' | 'desc' | '')} sortDir
	 * @memberof MDCtrlController
	 */
	public handleSortChange(sortField: string, sortDir: 'asc' | 'desc' | '') {
		//  无排序方向时重置排序
		this.store.sort = sortDir ? `${sortField},${sortDir}` : '';
		this.load({ srfsortreset: sortDir === '' });
	}

	/**
	 * 处理当前页码变化
	 *
	 * @param {number} currentPage
	 * @memberof MDCtrlController
	 */
	public handlePageChange(currentPage: number) {
		this.store.curPage = currentPage;
		this.load();
	}

	/**
	 * 处理分页数变化
	 *
	 * @param {number} pageSize
	 * @memberof MDCtrlController
	 */
	public handlePageSizeChange(pageSize: number) {
		this.store.limit = pageSize;
		this.load();
	}

	/**
	 * 分页数据变化
	 *
	 * @param args
	 * @memberof MDCtrlController
	 */
	public handlePaginationChange(args: any) {
		const { page, size } = args;
		this.store.curPage = page;
		this.store.limit = size;
		this.load();
	}

	/**
	 * 处理选中数据改变
	 *
	 * @param {IParam[]} selections
	 * @memberof MDCtrlController
	 */
	public handleSelectionChange(selections: IParam[]) {}

	/**
	 * 选中全部
	 *
	 * @memberof MDCtrlController
	 */
	public selectAll(): void {}

	/**
	 * 选中数据
	 *
	 * @param {IParam[]} items 需要选中的数据
	 * @param {boolean} [reverse] 是否反选
	 * @memberof MDCtrlController
	 */
	public selectItems(items: IParam[], reverse?: boolean): void {}

	/**
	 * 处理导出
	 *
	 * @protected
	 * @param {IParam[]} source
	 * @memberof MDCtrlController
	 */
	protected async handleExport(source: IParam[]) {
		const tHeader: string[] = [];
		const filterVal: string[] = [];
		const exportItems = this.model.dataExport
			? this.model.dataExport.exportItems
			: [];
		//  数据类型
		const colTypes: string[] = [];
		//  值格式化
		const colFormat: string[] = [];
		const getType = (type = ''): string => {
			//  目前仅支持识别数值、日期，其余类型视为字符串
			if (type) {
				if (DataTypeUtil.isDate(type)) {
					return 'date';
				} else if (DataTypeUtil.isNumber(type)) {
					return 'number';
				} else {
					return 'string';
				}
			}
			return 'string';
		};
		exportItems.forEach((item: IParam) => {
			if (item.show) {
				item.label ? tHeader.push(item.label) : '',
					filterVal.push(item.name.toLowerCase());
				colTypes.push(getType(item.dataType));
				if (item.valueFormat) {
					colFormat.push(item.valueFormat);
				} else {
					colFormat.push('');
				}
			}
		});
		const data = await this.formatExcelData(exportItems, filterVal, source);
		const excel = await ExportExcelUtil.getInstance().getExcel();
		excel.export_json_to_excel({
			header: tHeader, //表头 必填
			data, //具体数据 必填
			filename: this.model.dataExport ? this.model.dataExport.fileName : '', //非必填
			autoWidth: true, //非必填
			bookType: 'xlsx', //非必填
			colTypes,
			colFormat,
			sheetName: this.model.dataExport ? this.model.dataExport.fileName : '', //非必填
		});
	}

	/**
	 * 格式化导出数据
	 *
	 * @private
	 * @param {IParam[]} exportItems
	 * @param {string[]} filter
	 * @param {IParam[]} source
	 * @return {*}
	 * @memberof MDCtrlController
	 */
	private async formatExcelData(
		exportItems: IParam[],
		filter: string[],
		source: IParam[]
	) {
		for (const item of exportItems) {
			if (item.codeListTag) {
				const result = await App.getAppCodeListHelper().getCodeListItems(
					item.codeListTag
				);
				if (result.ok) {
					source.forEach((row: IParam, index: number) => {
						row[item.name] = this.parseCodeListValue(
							result.data,
							row[item.name],
							item
						);
					});
				}
			}
		}
		return source.map((v: any) => filter.map((j: any) => v[j]));
	}

	/**
	 * 解析导出项代码表值
	 *
	 * @private
	 * @param {IParam[]} items 代码表值
	 * @param {*} value 值
	 * @param {IParam} exportItem 导出项
	 * @return {*}  {string}
	 * @memberof MDCtrlController
	 */
	private parseCodeListValue(
		items: IParam[],
		value: any,
		exportItem: IParam
	): string {
		if (value || value === 0 || value === true) {
			let result: any[] = [];
			const values = Object.is(typeOf(value), 'string')
				? [...value.split(exportItem.valueSeparator || ',')]
				: [value];
			values.forEach((item: any) => {
				const codeListItem: IParam | undefined = items.find(
					(c: IParam) => c.value == value
				);
				if (codeListItem) {
					result.push(codeListItem.text);
				}
			});
			if (result.length > 0) {
				return result.join(exportItem.valueSeparator || ',');
			} else {
				return '';
			}
		} else {
			return '';
		}
	}

	/**
	 * 导出Excel
	 *
	 * @param {IParam} [opt={}]
	 * @return {*}  {Promise<ICtrlActionResult>}
	 * @memberof MDCtrlController
	 */
	public async exportExcel(opt: IParam = {}): Promise<ICtrlActionResult> {
		if (!this.actions.fetchAction) {
			return { ok: false, data: this.getData(), rowData: {} };
		}
		const { dataExport } = this.model;
		if (!dataExport) {
			return {
				ok: false,
				data: this.getData(),
				rowData: { status: 500 },
			};
		}
		if (dataExport.enableBackend && !dataExport.enableFront) {
			return await this.exportExcelByBackend(opt);
		}
		const { limit, curPage } = this.store;
		const queryParams: IParam = {};
		const maxCount = import.meta.env.VITE_APP_EXPORTMAXROWCOUNT || 9999;
		if (Object.is(opt.type, 'maxRowCount')) {
			Object.assign(queryParams, {
				page: 0,
				size: dataExport.maxRowCount ? dataExport.maxRowCount : maxCount,
			});
		} else if (Object.is(opt.type, 'activatedPage')) {
			Object.assign(queryParams, { page: curPage - 1, size: limit });
		} else if (Object.is(opt.type, 'custom')) {
			Object.assign(queryParams, {
				page: 0,
				offset: (opt.startPage - 1) * limit,
				size: (opt.endPage - opt.startPage + 1) * limit,
			});
		}
		//  排序
		const { noSort, sortField, sortDir } = this.model;
		if (!noSort && sortField) {
			Object.assign(queryParams, {
				sort: `${sortField},${sortDir ? sortDir : 'asc'}`,
			});
		}
		const parentData: IParam = {};
		this.emit('beforeLoad', [parentData]);
		//	视图查询参数
		if (parentData && Object.keys(parentData).length > 0) {
			Object.assign(queryParams, parentData);
		}
		const tempContext: IContext = deepCopy(this.store.context);
		Object.assign(queryParams, this.store.viewParams);
		const arg = { viewParams: queryParams };
		const res = await this.beforeAsyncAction('export', tempContext, arg);
		if (!res.ok) {
			return { ok: false, data: this.getData(), rowData: { status: 500 } };
		}
		try {
			const response = await this.ctrlService[
				dataExport.default ? 'search' : 'searchExportData'
			](this.actions.fetchAction, tempContext, arg);
			const res = await this.afterAsyncAction('export', response);
			if (!res.ok) {
				return { ok: false, data: this.getData(), rowData: { status: 500 } };
			}
			if (!response.success) {
				return {
					ok: false,
					data: this.getData(),
					rowData: response,
				};
			}
			this.handleExport(response.data);
			return { ok: true, data: this.getData(), rowData: response };
		} catch (error: any) {
			await this.afterAsyncAction('export', error);
			return {
				ok: false,
				data: this.getData(),
				rowData: error,
			};
		}
	}

	/**
	 * 后台导出
	 *
	 * @param {IParam} [opt]
	 * @memberof MDCtrlController
	 */
	public async exportExcelByBackend(opt?: IParam): Promise<ICtrlActionResult> {
		return {
			ok: false,
			data: this.getData(),
			rowData: { status: 200 },
		};
	}

	/**
	 * 刷新
	 *
	 * @param {(IParam | undefined)} [args]
	 * @return {*}  {Promise<boolean>}
	 * @memberof MDCtrlController
	 */
	public async refresh(args?: IParam | undefined): Promise<boolean> {
		try {
			const result = await this.load(args);
			if (result.ok) {
				return true;
			}
		} catch (error) {
			return false;
		}
		return false;
	}

	/**
	 * 获取部件能力
	 *
	 * @return {*}  {A}
	 * @memberof MDCtrlController
	 */
	public getAbility(): A {
		return {
			...super.getAbility(),
			load: this.load.bind(this),
			selectAll: this.selectAll.bind(this),
			selectItems: this.selectItems.bind(this),
			exportExcel: this.exportExcel.bind(this),
		};
	}
}
